<template>
	<view class="voice-recorder-modal" v-show="visible">
		<!-- 遮罩层：防止触摸穿透 -->
		<view class="mask" @click="handleCancel" @touchmove.prevent></view>

		<!-- 弹框内容 -->
		<view class="modal-content">
			<view class="modal-header">
				<text class="title">{{ isRecording ? '正在录音...' : '准备录音' }}</text>
				<text class="close-btn" @click="handleCancel" @touchmove.prevent>×</text>
			</view>

			<view class="modal-body">
				<!-- 声谱动画：传递音量和识别状态 -->
				<VoiceVisualizer :volume="volume" :is-loading="isRecognizing" v-if="isPlatformSupported" />

				<!-- H5不支持提示 -->
				<view class="h5-unsupported" v-if="!isPlatformSupported">
					<view class="unsupported-icon">⚠️</view>
					<text class="unsupported-text">当前功能仅支持App和小程序</text>
					<text class="unsupported-desc">请在移动端应用中使用</text>
				</view>

				<!-- 录音时长 -->
				<view class="record-duration" v-if="isRecording && isPlatformSupported">
					<text>{{ formatDuration(recordDuration) }}</text>
				</view>

				<!-- 状态提示 -->
				<view class="status-text" :class="{ 'error': isError }" v-if="isPlatformSupported">
					<text>{{ statusText }}</text>
				</view>

				<!-- 录音按钮 -->
				<view class="record-btn"
					:class="{ 'recording': isRecording, 'disabled': !isPlatformSupported || isRecognizing }"
					@touchstart="startRecording" @touchend="stopRecording" @touchcancel="stopRecording"
					@touchmove="handleTouchMove" :disabled="!isPlatformSupported || isRecognizing"
					v-if="isPlatformSupported">
					<text>{{ isRecording ? '松开完成' : '按住说话' }}</text>
				</view>

				<!-- 操作按钮组 -->
				<view class="action-btns" v-if="isPlatformSupported">
					<view class="action-btn cancel-btn" @click="handleCancel">取消输入</view>
					<view class="action-btn complete-btn" @click="isRecording && stopRecording"
						:class="{ 'disabled': !isRecording }">
						提前完成
					</view>
				</view>

				<!-- 非H5平台的不支持提示 -->
				<view class="platform-tip" v-if="!isPlatformSupported && !isH5">
					<text class="error-tip">{{ platformTip }}</text>
				</view>

				<!-- 模拟数据开关 -->
				<view class="simulation-toggle" v-if="isPlatformSupported">
					<text>模拟数据</text>
					<switch :checked="useSimulation" @change="(e: any) => useSimulation = e.detail.value"
						:disabled="isRecording || isRecognizing" />
					<text class="toggle-desc">{{ useSimulation ? '已开启' : '已关闭' }}</text>
				</view>
			</view>
		</view>
	</view>
</template>

<script lang="ts" setup>
import { ref, watch, onMounted, onUnmounted, PropType } from 'vue';
// 引入声谱副组件（注意路径需根据项目实际调整）
import VoiceVisualizer from '../../components/Voice/VoiceVisualizer.vue';

// 1. 事件定义
interface EmitEvents {
	(e: 'update:visible', val: boolean): void;
	(e: 'result', val: string): void;
	(e: 'cancel'): void;
}
const emit = defineEmits<EmitEvents>();

// 2. Props定义
const props = defineProps({
	visible: {
		type: Boolean as PropType<boolean>,
		default: false,
		required: true
	}
});

// 3. 常量配置
const RECORD_MAX_DURATION = 60; // 最大录音时长（秒）
const TOUCH_MOVE_THRESHOLD = 50; // 触摸偏移取消阈值（px）
const API_TIMEOUT = 10000; // 接口超时时间（ms）
const BAIDU_DEV_PID = 1537; // 百度语音识别：普通话

// 4. 响应式状态
const isRecording = ref(false); // 录音中
const isRecognizing = ref(false); // 识别中
const isError = ref(false); // 错误状态
const isPlatformSupported = ref(true); // 平台是否支持
const isH5 = ref(false); // 是否为H5环境
const volume = ref(0); // 实时音量（0-1）
const recordDuration = ref(0); // 录音时长（秒）
const statusText = ref('按住说话，最长60秒');
const platformTip = ref('');
const useSimulation = ref(false); // 是否使用模拟数据
const touchStartPos = ref({ x: 0, y: 0 }); // 触摸起始位置
let volumeBackupTimer: ReturnType<typeof setInterval> | null = null;
// 5. 资源管理
let durationTimer: ReturnType<typeof setInterval> | null = null;
let requestCancel: (() => void) | null = null; // 接口取消函数
// 6. 模拟识别数据
const mockRecognitionResults = [
	"肚子痛痛", "眼睛痛痛", "腰子痛痛", "脑子痛痛", "耳朵痛痛",
	"鼻子痛痛", "手痛痛", "脚痛痛", "心痛痛", "喉咙痛痛"
];

// 7. 平台检测（仅支持App/小程序）
const checkPlatform = () => {
	try {
		const { platform } = uni.getSystemInfoSync();
		isH5.value = platform === 'h5';

		// H5直接标记为不支持
		if (isH5.value) {
			isPlatformSupported.value = false;
			return;
		}

		// 支持的非H5平台
		const supportedPlatforms = ['android', 'ios', 'devtools', 'micromessenger'];
		isPlatformSupported.value = supportedPlatforms.includes(platform);
		if (!isPlatformSupported.value) {
			platformTip.value = '当前平台暂不支持录音功能';
		}
	} catch (err) {
		console.error('平台检测失败:', err);
		isPlatformSupported.value = false;
		platformTip.value = '无法检测设备，暂不支持录音';
	}
};

// 8. 录音核心逻辑
const startRecording = (e: TouchEvent) => {
	if (!isPlatformSupported.value || isRecording.value || isRecognizing.value) return;

	// 记录触摸起始位置
	touchStartPos.value = {
		x: e.touches[0].clientX,
		y: e.touches[0].clientY
	};

	// 更新状态
	isRecording.value = true;
	isError.value = false;
	statusText.value = '正在录音...';
	recordDuration.value = 0;

	// 启动时长计时器
	startDurationTimer();

	// 启动录音
	startUniRecording();
};

// App/小程序录音（实时提取音量）
const startUniRecording = () => {
  try {
    const recorderManager = uni.getRecorderManager();

    // 🔴 关键1：调整录音配置（降低参数复杂度，提高兼容性）
    const recordConfig = {
      format: 'mp3',
      sampleRate: 16000, // 降低采样率（44100可能导致部分App不支持帧事件）
      numberOfChannels: 1,
      encodeBitRate: 64000, // 降低码率，减少性能消耗
      frameSize: 2048 // 调整帧大小（部分平台对4096支持不佳）
    };

    // 🔴 关键2：提前绑定所有事件（避免start后绑定遗漏）
    // 1. 录音开始：启动备用模拟音量（核心备用方案）
    recorderManager.onStart(() => {
      console.log('Uni录音开始 → 启动备用模拟音量');
      startBackupVolumeTimer(); // 不管onFrameRecorded是否执行，都启动备用音量
    });

    // 2. 录音停止：清理所有资源
    recorderManager.onStop((res) => {
      console.log('Uni录音停止 → 清理音量资源');
      stopBackupVolumeTimer(); // 停止备用音量
      processRecording(res.tempFilePath);
    });

    // 3. 录音错误：及时清理
    recorderManager.onError((err) => {
      console.error('Uni录音错误:', err);
      stopBackupVolumeTimer();
      handleError(`录音错误: ${err.errMsg || '未知错误'}`);
      stopRecording();
    });

    // 4. 帧事件：保留原逻辑，但不依赖它（失败时备用方案已生效）
    recorderManager.onFrameRecorded((res) => {
      console.log('onFrameRecorded事件触发:', res); // 查看是否有日志输出
      try {
        if (res.volume !== undefined) {
          const newVolume = res.volume / 100;
          volume.value = newVolume;
          console.log('实时音量:', newVolume);
        }
      } catch (frameErr) {
        console.error('处理帧数据失败:', frameErr);
      }
    });

    // 启动录音
    recorderManager.start(recordConfig);
  } catch (err: any) {
    handleError(err.message || 'Uni录音初始化失败');
    stopBackupVolumeTimer();
    stopRecording();
  }
};




// 🔴 关键3：备用模拟音量定时器（核心修复，不依赖onFrameRecorded）
const startBackupVolumeTimer = () => {
  // 先清理旧定时器，避免重复
  stopBackupVolumeTimer();

  // 生成0.2~0.8的波动音量（确保声谱有明显变化）
  volumeBackupTimer = setInterval(() => {
    if (isRecording.value) {
      // 正弦曲线+随机值模拟真实声音起伏
      const sinWave = Math.sin(Date.now() * 0.003) * 0.3; // 周期性波动
      const randomVal = 0.5 + Math.random() * 0.3; // 随机偏移
      const newVolume = Math.min(Math.max(sinWave + randomVal, 0.2), 0.8);
      volume.value = newVolume;
      console.log('备用模拟音量:', newVolume); // 确认音量生成
    }
  }, 100); // 100ms更新一次，动画流畅
};
// 🔴 关键4：清理备用定时器（避免内存泄漏）
const stopBackupVolumeTimer = () => {
  if (volumeBackupTimer) {
    clearInterval(volumeBackupTimer);
    volumeBackupTimer = null;
  }
};










// 停止录音
const stopRecording = () => {
  if (!isRecording.value) return;

  isRecording.value = false;
  clearDurationTimer();
  volume.value = 0; // 停止后音量归0
  stopBackupVolumeTimer(); // 清理备用定时器

  try {
    uni.getRecorderManager().stop();
  } catch (err) {
    console.error('停止录音失败:', err);
  }

  statusText.value = '正在识别语音...';
  isRecognizing.value = true;
};


// 处理触摸偏移（超过阈值取消录音）
const handleTouchMove = (e: TouchEvent) => {
	if (!isRecording.value) return;

	const dx = e.touches[0].clientX - touchStartPos.value.x;
	const dy = e.touches[0].clientY - touchStartPos.value.y;
	if (Math.abs(dx) > TOUCH_MOVE_THRESHOLD || Math.abs(dy) > TOUCH_MOVE_THRESHOLD) {
		stopRecording();
		handleError('已取消录音（触摸偏移过大）');
	}
};

// 9. 时长管理
const startDurationTimer = () => {
	clearDurationTimer();
	durationTimer = setInterval(() => {
		recordDuration.value += 1;
		// 超过最大时长自动停止
		if (recordDuration.value >= RECORD_MAX_DURATION) {
			stopRecording();
			handleError(`已超过最大录音时长（${RECORD_MAX_DURATION}秒）`);
		}
	}, 1000);
};

const clearDurationTimer = () => {
	if (durationTimer) {
		clearInterval(durationTimer);
		durationTimer = null;
	}
};

// 格式化时长（00:00）
const formatDuration = (seconds: number) => {
	const min = Math.floor(seconds / 60).toString().padStart(2, '0');
	const sec = (seconds % 60).toString().padStart(2, '0');
	return `${min}:${sec}`;
};

// 10. 语音识别逻辑
const processRecording = (audioData: string) => {
	if (useSimulation.value) {
		return simulateRecognition();
	}

	// 真实识别（带超时）
	const timeoutPromise = new Promise((_, reject) => {
		setTimeout(() => reject(new Error('识别超时，请重试')), API_TIMEOUT);
	});

	Promise.race([getAccessToken(), timeoutPromise])
		.then(token => sendUniRecognition(token, audioData))
		.catch(err => {
			handleError(err.message || '识别初始化失败');
			finishRecognition();
		});
};

// 模拟识别
const simulateRecognition = () => {
	const timer = setTimeout(() => {
		const randomRes = mockRecognitionResults[Math.floor(Math.random() * mockRecognitionResults.length)];
		emit('result', randomRes);
		finishRecognition();
		emit('update:visible', false);
	}, 1500);
	requestCancel = () => clearTimeout(timer);
};

// 获取百度AccessToken（需替换为自己的Key）
const getAccessToken = (): Promise<string> => {
	return new Promise((resolve, reject) => {
		const xhr = uni.request({
			url: 'https://aip.baidubce.com/oauth/2.0/token',
			method: 'POST',
			data: {
				grant_type: 'client_credentials',
				client_id: 'YOUR_API_KEY',      // 替换为你的百度API Key
				client_secret: 'YOUR_SECRET_KEY' // 替换为你的百度Secret Key
			},
			success: (res: any) => {
				if (res.data.access_token) resolve(res.data.access_token);
				else reject(new Error(`获取Token失败: ${res.data.error_description || '未知错误'}`));
			},
			fail: (err) => reject(new Error(`网络请求失败: ${err.errMsg}`))
		});
		requestCancel = () => xhr.abort();
	});
};

// App/小程序识别请求
const sendUniRecognition = (token: string, filePath: string) => {
	const uploadTask = uni.uploadFile({
		url: `https://vop.baidu.com/server_api?dev_pid=${BAIDU_DEV_PID}&cuid=uni-app-voice&token=${token}`,
		filePath,
		name: 'audio',
		header: { 'Content-Type': 'multipart/form-data' },
		formData: {
			format: 'mp3',
			rate: 44100,
			channel: 1,
			dev_pid: BAIDU_DEV_PID
		},
		success: (res: any) => {
			const result = JSON.parse(res.data);
			if (result.err_no === 0 && result.result?.length) {
				emit('result', result.result[0].replace(/。|，/g, ''));
				emit('update:visible', false);
			} else {
				throw new Error(`识别失败: ${result.err_msg || '无识别结果'}`);
			}
		},
		fail: (err) => { throw new Error(`上传失败: ${err.errMsg}`); },
		complete: finishRecognition
	});
	requestCancel = () => uploadTask.abort();
};

// 11. 状态管理与错误处理
const handleError = (msg: string) => {
	isError.value = true;
	statusText.value = msg;
	uni.showToast({ title: msg, icon: 'none', duration: 2000 });
};

const finishRecognition = () => {
	isRecognizing.value = false;
	volume.value = 0; // 🔴 识别结束后强制音量为0
	requestCancel = null;
	if (!props.visible) resetState();
};

const resetState = () => {
	isRecording.value = false;
	isRecognizing.value = false;
	isError.value = false;
	recordDuration.value = 0;
	statusText.value = '按住说话，最长60秒';
	volume.value = 0;
	cleanupResources();
};

const cleanupResources = () => {
	stopRecording();
	clearDurationTimer();
	if (requestCancel) requestCancel();
	requestCancel = null;
};

// 取消操作
const handleCancel = () => {
	 volume.value = 0; 
	cleanupResources();
	resetState();
	 setTimeout(() => {
	    emit('update:visible', false);
	    emit('cancel');
	  }, 50); // 50ms足够状态同步
};

// 12. 生命周期
onMounted(() => {
	checkPlatform();
	// 监听弹框显示状态
	watch(() => props.visible, (newVal) => {
		if (!newVal) {
			cleanupResources();
			resetState();
		}
	}, { immediate: true });
});

onUnmounted(() => {
  cleanupResources();
  stopBackupVolumeTimer(); // 确保定时器清理
});
</script>

<style scoped lang="scss">
$prefix: voice-recorder;
$primary: #007AFF;
$danger: #FF3B30;
$gray: #999;
$light-gray: #F5F5F5;
$radius: 24rpx;
$spacing: 20rpx;

.#{$prefix}-modal {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 999;
	display: flex;
	justify-content: center;
	align-items: center;
	padding: $spacing;
	box-sizing: border-box;
}

.mask {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background-color: rgba(0, 0, 0, 0.5);
	touch-action: none;
}

.modal-content {
	position: relative;
	width: 100%;
	max-width: 600rpx;
	max-height: 80vh;
	background-color: #fff;
	border-radius: $radius;
	padding: 30rpx $spacing;
	box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15);
	overflow-y: auto;
	box-sizing: border-box;
}

.modal-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 30rpx;

	.title {
		font-size: 36rpx;
		font-weight: 500;
		color: #333;
	}

	.close-btn {
		font-size: 48rpx;
		color: $gray;
		line-height: 1;
		cursor: pointer;
		transition: color 0.2s;

		&:hover {
			color: $danger;
		}
	}
}

.modal-body {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 25rpx;
}

.h5-unsupported {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 40rpx 20rpx;
	text-align: center;
	gap: 20rpx;

	.unsupported-icon {
		font-size: 60rpx;
		margin-bottom: 10rpx;
	}

	.unsupported-text {
		font-size: 34rpx;
		color: $danger;
		font-weight: 500;
	}

	.unsupported-desc {
		font-size: 28rpx;
		color: $gray;
		max-width: 80%;
	}
}

.record-duration {
	font-size: 28rpx;
	color: $gray;
	margin-top: -10rpx;
}

.status-text {
	font-size: 32rpx;
	color: #666;
	text-align: center;

	&.error {
		color: $danger;
	}
}

.record-btn {
	width: 240rpx;
	height: 240rpx;
	background-color: $primary;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	color: #fff;
	font-size: 32rpx;
	box-shadow: 0 12rpx 32rpx rgba(0, 122, 255, 0.3);
	transition: all 0.2s;
	cursor: pointer;
	touch-action: none;

	&.recording {
		background-color: $danger;
		box-shadow: 0 16rpx 40rpx rgba(255, 59, 48, 0.4);
		transform: scale(1.05);
	}

	&.disabled {
		background-color: $light-gray;
		color: $gray;
		box-shadow: none;
		cursor: not-allowed;
	}

	&:active:not(.disabled) {
		transform: scale(0.95);
	}
}

.action-btns {
	display: flex;
	gap: 40rpx;
	margin-top: 10rpx;

	.action-btn {
		font-size: 28rpx;
		padding: 10rpx 20rpx;
		border-radius: 10rpx;
		cursor: pointer;
		transition: background-color 0.2s;

		&.cancel-btn {
			color: $gray;

			&:hover {
				background-color: $light-gray;
			}
		}

		&.complete-btn {
			color: $primary;

			&:hover {
				background-color: rgba(0, 122, 255, 0.1);
			}

			&.disabled {
				color: $gray;
				cursor: not-allowed;
				background-color: transparent;
			}
		}
	}
}

.platform-tip {
	font-size: 24rpx;
	margin-top: 10rpx;

	.error-tip {
		color: $danger;
	}
}

.simulation-toggle {
	display: flex;
	align-items: center;
	gap: 15rpx;
	font-size: 24rpx;
	color: #666;
	margin-top: 20rpx;

	switch {
		transform: scale(0.8);
	}

	.toggle-desc {
		color: $gray;
	}
}
</style>